use spin::RwLock;
use unmp::id::Id;
use unmp_center_core::{Cmd, Data};

static LISTENER: RwLock<Listener> = RwLock::new(Listener::new());

type VarChgCb = fn(&Id, u16, &[u8]);
type BindCb = fn(&Id, &[u8; 8]);
type UnbindCb = fn(&Id);

#[derive(Default)]
struct Listener {
    actual: Option<VarChgCb>,
    target: Option<VarChgCb>,
    bind: Option<BindCb>,
    unbind: Option<UnbindCb>,
}
impl Listener {
    const fn new() -> Listener {
        Listener {
            actual: None,
            target: None,
            bind: None,
            unbind: None,
        }
    }
}

/// 接收通知
pub(crate) fn recv(data: &Data) {
    match data.cmd() {
        Cmd::NoticeActual { id, k, v } => emit_actual_change(id, *k, &v),
        Cmd::NoticeTarget { id, k, v } => emit_target_change(id, *k, &v),
        Cmd::NoticeBind { id, public_key } => emit_bind(id, public_key),
        Cmd::NoticeUnbind { id } => emit_unbind(id),
        _ => {}
    }
}

pub fn on_actual_change(cb: VarChgCb) {
    LISTENER.write().actual = Some(cb);
}
fn emit_actual_change(id: &Id, key: u16, val: &[u8]) {
    if let Some(cb) = LISTENER.read().actual {
        cb(id, key, val);
    }
}
pub fn on_target_change(cb: VarChgCb) {
    LISTENER.write().target = Some(cb);
}
fn emit_target_change(id: &Id, key: u16, val: &[u8]) {
    if let Some(cb) = LISTENER.read().target {
        cb(id, key, val);
    }
}
pub fn on_bind(cb: BindCb) {
    LISTENER.write().bind = Some(cb);
}
fn emit_bind(id: &Id, public_key: &[u8; 8]) {
    if let Some(cb) = LISTENER.read().bind {
        cb(id, public_key);
    }
}
pub fn on_unbind(cb: UnbindCb) {
    LISTENER.write().unbind = Some(cb);
}
fn emit_unbind(id: &Id) {
    if let Some(cb) = LISTENER.read().unbind {
        cb(id);
    }
}
